﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using IEx.Common;
using IEx.Common.Sources;
using IEx.Downloader;
using IEx.Model;
using IEx.Model.Partials;
using IEx.Model.Partials.Media;
using IEx.Utilities;

namespace IEx
{
    public interface IStore
    {
        bool Save(Announcement item);
    }

    public class DefaultStore : IStore
    {
        private FileDownloader downloader;
        private Repository repository;

        /// <summary>
        /// 
        /// </summary>
        public DefaultStore()
        {
            downloader = new FileDownloader();
            repository = new Repository();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Save(Announcement item)
        {
            try
            {

                if ((int)SourceIds.Youtube == item.SourceId)
                {
                    return SaveYoutubeVideo(item);
                }
                else if (item.SourceId == (int)SourceIds.HKExNews)
                {
                    return SaveHKExNewAnnouncements(item);
                }
                else if ((int)SourceIds.Tamtay == item.SourceId)
                {
                    return SavePhoto(item);
                }
                else
                {
                    return SaveData2XmlFile(item);
                }                
            }
            catch (Exception ex)
            {
                Logger.WriteError("IStore:(" + item.Title + ") ", ex);
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        private bool SaveData2XmlFile(Announcement item)
        {
            try
            {
                /*foreach (string link in item.AttachmentLinks)
                {
                    HttpStatusCode filestatus = HttpStatusCode.Accepted;
                    string downloadedFile = downloader.Download(item, link, ref filestatus);
                    if (filestatus != HttpStatusCode.NotFound
                        && !string.IsNullOrEmpty(downloadedFile)
                        && File.Exists(downloadedFile))
                    {
                        
                    }
                }*/
                Utility.SaveAnnouncementToXml(item);
                Utility.DeleteFailAnnouncement(item.FailId);
                return true;
            }
            catch (Exception ex)
            {
                Logger.WriteError(string.Format("Download (cCode: {0}): {1}", item.SourceCode, item.Link), ex);
                Utility.SaveFailAnnouncement(item);
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        private bool SaveYoutubeVideo(Announcement item)
        {
            if (item.Id > 0)
            {
                return InsertVideoAttachment(item);
            }
            else
            {
                if (item.Message != null)
                {
                    item.Id = repository.InsertPressreleases(item);
                }

                if (item.Id <= 0)
                {
                    item.FailId = DateTime.Now.Ticks;
                    Utility.SaveFailAnnouncement(item);
                    return false;
                }
                // inserts news into database successfully
                else if (item.Id > 0)
                {
                    return InsertVideoAttachment(item);
                }
                return true;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        private bool SavePhoto(Announcement item)
        {
            if (item.Id > 0)
            {
                return InsertVideoAttachment(item);
            }
            else
            {
                if (item.Message != null)
                {
                    item.Id = repository.InsertPressreleases(item);
                }

                if (item.Id <= 0)
                {
                    item.FailId = DateTime.Now.Ticks;
                    Utility.SaveFailAnnouncement(item);
                    return false;
                }
                // inserts news into database successfully
                else if (item.Id > 0)
                {
                    return SavePhotoAttachment(item);
                }
                return true;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        private bool InsertVideoAttachment(Announcement item)
        {
            try
            {
                HttpStatusCode readHtmlFileStatus = HttpStatusCode.Accepted;
                VideoInfo video = item.SocialNetwork.Videos.First(info => info.VideoType == VideoType.Mp4 && info.Resolution == 360);
                if (video.RequiresDecryption)
                {
                    Utility.DecryptDownloadUrl(video);
                }
                readHtmlFileStatus = HttpStatusCode.Accepted;
                string date = DateTime.Now.Year + "-" + DateTime.Now.Month.ToString("00") + "-" + DateTime.Now.Day.ToString("00") + "-" + DateTime.Now.Date.Hour.ToString("00");
                string folder = Utility.GetSourceCodeFolderName(item.SourceId);
                string directoryPath = Path.Combine(Utility.DownloadedFolderPath, item.SourceCode, folder, date);
                if (!Directory.Exists(directoryPath))
                    Directory.CreateDirectory(directoryPath);
                downloader.DownloadProgressChanged += (sender, args) => Logger.Debug("***Downloading: " + args.ProgressPercentage);
                string fileName = Utility.RemoveIllegalPathCharacters(video.Title) + video.VideoExtension;
                string downloadedFile = downloader.Download(item, fileName, video.DownloadUrl, directoryPath);
                Logger.WriteLogInfo("***DONE DOWNLOADING VIDEO URL: " + video.DownloadUrl + " ---FILE NAME: " + video.Title);

                if (readHtmlFileStatus != HttpStatusCode.NotFound
                  && readHtmlFileStatus != HttpStatusCode.RequestTimeout
                  && !string.IsNullOrEmpty(downloadedFile))
                {

                    Attachment attachment = new Attachment();
                    attachment.PressreleaseId = item.Id;
                    attachment.MediaBinary = File.ReadAllBytes(downloadedFile);
                    attachment.IsBlob = true;
                    attachment.Published = true;
                    attachment.FileName = fileName;
                    attachment.Location = downloadedFile.Replace(Utility.DownloadedFolderPath, "").TrimStart('\\');
                    attachment.DisplayOrder = 0;
                    attachment.LanguageId = item.LangId;
                    attachment.MineType = Utility.GetMIME(Path.GetExtension(fileName));
                    repository.InsertAttachment(attachment);
                }

                Utility.DeleteFailAnnouncement(item.FailId);
                return true;
            }
            catch (Exception ex)
            {
                Logger.WriteError(string.Format("Download (cCode: {0}): {1}", item.SourceCode, item.Link), ex);
                Utility.SaveFailAnnouncement(item);
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        private bool SavePhotoAttachment(Announcement item)
        {
            try
            {
                foreach (Photo ph in item.SocialNetwork.Photos)
                {
                    HttpStatusCode filestatus = HttpStatusCode.Accepted;
                    string downloadedFile = downloader.Download(item, ph.AttachmentLinks, ref filestatus);
                    if (filestatus != HttpStatusCode.NotFound
                        && !string.IsNullOrEmpty(downloadedFile)
                        && File.Exists(downloadedFile))
                    {
                        Attachment attachment = new Attachment();
                        attachment.PressreleaseId = item.Id;
                        attachment.MediaBinary = File.ReadAllBytes(downloadedFile);
                        attachment.IsBlob = true;
                        attachment.Published = true;
                        attachment.FileName = Path.GetFileName(downloadedFile);
                        attachment.Location = downloadedFile.Replace(Utility.DownloadedFolderPath, "").TrimStart('\\');
                        attachment.DisplayOrder = 0;
                        attachment.LanguageId = item.LangId;
                        attachment.MineType = Utility.GetMIME(Path.GetExtension(downloadedFile));
                        repository.InsertAttachment(attachment);

                    }
                }

                Utility.DeleteFailAnnouncement(item.FailId);
                return true;
            }
            catch (Exception ex)
            {
                Logger.WriteError(string.Format("Download (cCode: {0}): {1}", item.SourceCode, item.Link), ex);
                Utility.SaveFailAnnouncement(item);
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="announcement"></param>
        /// <returns></returns>
        private bool SaveHKExNewAnnouncements(Announcement announcement)
        {
            // Hongkong has different way to get MessageTypeID
            if (announcement.SourceId == (int)SourceIds.HKExNews)
            {
                string prType = announcement.PressrelaseType;
                string messageType = announcement.PressrelaseType;
                string messageGroup = string.Empty;

                int lastOpenBraket = prType.LastIndexOf('[');
                if (lastOpenBraket >= 0)
                {
                    int lastCloseBraket = prType.LastIndexOf(']');
                    if (lastCloseBraket < lastOpenBraket) lastCloseBraket = prType.Length;
                    messageType = prType.Substring(lastOpenBraket + 1, lastCloseBraket - lastOpenBraket - 1);
                    messageType = messageType.Split('/')[0].Trim();

                    int lastDashIndex = prType.Substring(0, lastOpenBraket).LastIndexOf('-');
                    messageGroup = prType.Substring(0, lastDashIndex).Trim();
                }

                // Get MesssageType from DB
                MessageType mType = repository.GetMessageType(SourceIds.HKExNews, messageType, messageGroup);
                announcement.MessageTypeId = mType.MessageTypeId;
            }

            // Only read content from attachment to insert into DB when a pressrelease has only 1 attachment
            if (announcement.AttachmentLinks.Count == 1)
            {
                HttpStatusCode fileStatus = HttpStatusCode.Accepted;

                // Get Message from Pdf file to insert into DB
                foreach (string link in announcement.AttachmentLinks)
                {
                    string filePath = downloader.Download(announcement, link, ref fileStatus);
                    if (fileStatus != HttpStatusCode.NotFound
                        && fileStatus != HttpStatusCode.RequestTimeout
                        && !string.IsNullOrEmpty(filePath)
                        && File.Exists(filePath) && filePath.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
                    {
                        announcement.Message = Utility.ReadPdfFile(filePath) ?? "";
                    }

                    break;
                }
            }


            return this.InternalSaveAnnoucement(announcement);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="announcement"></param>
        /// <returns></returns>
        private bool InternalSaveAnnoucement(Announcement announcement)
        {
            // If this pressrelease is listed in ignored list, just return true as it works correct
            if (Utility.IgnoreAnnouncements.Any(ignoreAnn => ignoreAnn.Trim().Equals(announcement.Title.Trim(), StringComparison.OrdinalIgnoreCase)))
            {
                return true;
            }

            if (announcement.Updated)
            {
                return ReloadPressrelease(announcement);
            }
            else
            {
                return InsertNewPressrelease(announcement);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="announcement"></param>
        /// <returns></returns>
        private bool InsertNewPressrelease(Announcement announcement)
        {
            if (announcement.Id > 0)
            {
                try
                {
                    List<string> lstAttachmentLinks = new List<string>();

                    if (announcement.AttachmentLinks != null)
                        lstAttachmentLinks.AddRange(announcement.AttachmentLinks);
                    for (int attIndex = lstAttachmentLinks.Count - 1; attIndex >= 0; attIndex--)
                    {
                        string attachmentLink = lstAttachmentLinks[attIndex];
                        announcement.Link = attachmentLink;


                        HttpStatusCode filestatus = HttpStatusCode.Accepted;
                        string downloadedFile = downloader.Download(announcement, attachmentLink, ref filestatus);
                        if (filestatus != HttpStatusCode.NotFound
                            && !string.IsNullOrEmpty(downloadedFile)
                            && File.Exists(downloadedFile))
                        {
                            Attachment attachment = new Attachment();
                            attachment.PressreleaseId = announcement.Id;
                            attachment.MediaBinary = File.ReadAllBytes(downloadedFile);
                            attachment.IsBlob = true;
                            attachment.Published = true;
                            attachment.FileName = Path.GetFileName(downloadedFile);
                            attachment.Location = downloadedFile.Replace(Utility.DownloadedFolderPath, "").TrimStart('\\');
                            attachment.DisplayOrder = Utility.GetAttachmentOrder(downloadedFile, (SourceIds)announcement.SourceId, byte.Parse(attIndex.ToString()));
                            attachment.LanguageId = announcement.LangId;
                            attachment.MineType = Utility.GetMIME(Path.GetExtension(downloadedFile));
                            repository.InsertAttachment(attachment);

                        }

                        if (filestatus == HttpStatusCode.Accepted)
                        {
                            announcement.AttachmentLinks.Remove(attachmentLink);
                        }
                    }

                    Utility.DeleteFailAnnouncement(announcement.FailId);
                    return true;
                }
                catch (Exception ex)
                {
                    Logger.WriteError(string.Format("Download (cCode: {0}): {1}", announcement.SourceCode, announcement.Link), ex);
                    Utility.SaveFailAnnouncement(announcement);
                    return false;
                }
            }
            else
            {
                if (announcement.Message != null)
                {
                    announcement.Id = repository.InsertPressreleases(announcement);
                }

                if (announcement.Id <= 0)
                {
                    announcement.FailId = DateTime.Now.Ticks;
                    Utility.SaveFailAnnouncement(announcement);
                    return false;
                }
                // inserts news into database successfully
                else if (announcement.Id > 0)
                {
                    List<string> lstAttachmentLinks = new List<string>();

                    if (announcement.AttachmentLinks != null)
                        lstAttachmentLinks.AddRange(announcement.AttachmentLinks);

                    // loop through list of attachments
                    for (int attIndex = lstAttachmentLinks.Count - 1; attIndex >= 0; attIndex--)
                    {
                        string attachmentLink = lstAttachmentLinks[attIndex];

                        announcement.Link = attachmentLink;

                        long attachmentId = -1;
                        HttpStatusCode filestatus = HttpStatusCode.Accepted;
                        string downloadedFile = downloader.Download(announcement, attachmentLink, ref filestatus);
                        if (filestatus != HttpStatusCode.NotFound
                            && filestatus != HttpStatusCode.RequestTimeout
                            && !string.IsNullOrEmpty(downloadedFile)
                            && File.Exists(downloadedFile))
                        {
                            Attachment attachment = new Attachment();
                            attachment.PressreleaseId = announcement.Id;
                            attachment.MediaBinary = File.ReadAllBytes(downloadedFile);
                            attachment.IsBlob = true;
                            attachment.Published = true;
                            attachment.FileName = Path.GetFileName(downloadedFile);
                            attachment.Location = downloadedFile.Replace(Utility.DownloadedFolderPath, "").TrimStart('\\');
                            attachment.DisplayOrder = Utility.GetAttachmentOrder(downloadedFile, (SourceIds)announcement.SourceId, byte.Parse(attIndex.ToString()));
                            attachment.LanguageId = announcement.LangId;
                            attachment.MineType = Utility.GetMIME(Path.GetExtension(downloadedFile));
                            attachmentId = repository.InsertAttachment(attachment);
                        }

                        if ((filestatus != HttpStatusCode.Accepted
                            && filestatus != HttpStatusCode.NotFound)
                            || attachmentId == 0)
                        {
                            announcement.FailId = DateTime.Now.Ticks;
                            Utility.SaveFailAnnouncement(announcement);
                        }
                        else
                        {
                            announcement.AttachmentLinks.Remove(attachmentLink);
                        }
                    }
                    return true;
                }
                return true;
            }
        }

        /// <summary>
        /// Reload and update information and attachment for a pressrelease. This method is used only when FeedApp runs in Update mod
        /// </summary>
        /// <param name="announcement"></param>
        /// <returns></returns>
        private bool ReloadPressrelease(Announcement announcement)
        {
            long pressreleaseId = 0;
            // Update pressrelease
            pressreleaseId = repository.UpdatePressrelease(announcement);

            // Does not exist this pressrelease in DB? just insert with a new
            if (pressreleaseId <= 0)
            {
                repository.InsertPressreleases(announcement);
            }

            // Just update attachment if pressrelease is existing in DB
            if (pressreleaseId > 0)
            {
                try
                {
                    List<string> lstAttachmentLinks = new List<string>();
                    List<Attachment> availableAttachments = repository.GetPressreleaseAttachments(pressreleaseId, announcement.LangId);

                    // Try to find any attachment in DB but does not exist a physical file on hard-disk. This attachment should delete from DB
                    int length = availableAttachments.Count, i = 0;
                    string downloadFolder = FileDownloader.DownloadedFolderPath;
                    for (; i < length; i++)
                    {
                        if (string.IsNullOrEmpty(availableAttachments[i].Location)
                            || !File.Exists(Path.Combine(downloadFolder, availableAttachments[i].Location)))
                        {
                            if (repository.DeleteAttachment(availableAttachments[i].Id))
                            {
                                availableAttachments.RemoveAt(i);
                                --length;
                            }
                        }
                    }

                    if (announcement.AttachmentLinks != null)
                        lstAttachmentLinks.AddRange(announcement.AttachmentLinks);

                    // Search for new attachments to insert into DB
                    for (int attIndex = lstAttachmentLinks.Count - 1; attIndex >= 0; attIndex--)
                    {
                        string attachmentLink = lstAttachmentLinks[attIndex];
                        announcement.Link = attachmentLink;

                        HttpStatusCode filestatus = HttpStatusCode.Accepted;
                        string downloadedFile = downloader.Download(announcement, attachmentLink, ref filestatus);
                        if (filestatus != HttpStatusCode.NotFound
                            && !string.IsNullOrEmpty(downloadedFile)
                            && File.Exists(downloadedFile))
                        {
                            Attachment attachment = new Attachment();
                            attachment.PressreleaseId = pressreleaseId;
                            attachment.MediaBinary = File.ReadAllBytes(downloadedFile);
                            attachment.IsBlob = true;
                            attachment.Published = true;
                            attachment.FileName = Path.GetFileName(downloadedFile);
                            attachment.Location = downloadedFile.Replace(Utility.DownloadedFolderPath, "").TrimStart('\\');
                            attachment.DisplayOrder = Utility.GetAttachmentOrder(downloadedFile, (SourceIds)announcement.SourceId, int.Parse(attIndex.ToString()));
                            attachment.LanguageId = announcement.LangId;
                            attachment.MineType = Utility.GetMIME(Path.GetExtension(downloadedFile));
                            repository.InsertAttachment(attachment);
                        }
                    }

                    return true;
                }
                catch (Exception ex)
                {
                    Logger.WriteError(string.Format("Download (cCode: {0}): {1}", announcement.SourceCode, announcement.Link), ex);
                }
            }
            return false;
        }
    }
}
